//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "CTextureConverter.h"
#include "ILWrapper.h"
#include "..\\..\\Core\\CConverter.h"



//--------------------------- CTexture1D --------------------------//

CTexture1D::CTexture1D():
	_eFormat(e_tex_format::UNKNOWN),
	_bCompressed(false),	_eSwizzle(e_tex_swizzle::AUTO),
	_bMipmaps(false),		_bUseCompression(false)
{
}


CTexture1D::~CTexture1D()
{
}


HRESULT CTexture1D::SetImage(const char *pName)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage1D	s_image;

	if ( !SYSTEMS->pIL->GetTexture1D( s_image ) )
		return E_FAIL;

	_aImages.Clear();
	_aImages.PushFront( TImage1DExt( s_image, pName ) );
	_aImages.Resize( GetPowOfTwo( s_image.uLength ) );
	_eFormat			= s_image.eFormat;
	_eSwizzle			= s_image.eSwizzle;
	_bCompressed		= s_image.bCompressed;
	_bUseCompression	= _bCompressed;
	_bMipmaps			= false;
	return S_OK;
}


HRESULT CTexture1D::AddLayer(const char *pName, uint &uLayer)
{
	return S_FALSE;
}


HRESULT CTexture1D::AddMipmap(const char *pName, uint uLayer, uint &uMM)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage1D	s_image;

	if ( !SYSTEMS->pIL->GetTexture1D( s_image ) )
		return E_FAIL;

	int	i_level = GetPowOfTwo( _aImages.Front().sImage.uLength ) - GetPowOfTwo( s_image.uLength );

	if ( !Image::ComparePixelFormats( s_image.eFormat, _eFormat ) ||
		 s_image.bCompressed != _bCompressed || i_level <= 0 )
	{
		return E_FAIL;
	}

	uMM = i_level;
	_aImages[i_level] = TImage1DExt( s_image, pName );
	return S_OK;
}


HRESULT CTexture1D::GetName(uint uLayer, uint uMipmap, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 ||
		 uLayer != 0 || uMipmap >= _aImages.Count() )
	{
		return E_INVALIDARG;
	}

	if ( uCount > _aImages[uMipmap].sName.Length() )
	{
		memcpy( pBuffer, _aImages[uMipmap].sName.cstr(), _aImages[uMipmap].sName.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CTexture1D::Save(IFilePtr pFile)
{
	if ( _aImages.Front().sImage.aData.Empty() || _aImages.Front().sImage.aMipMaps.Empty() ||
		 _aImages.Front().sImage.eFormat == 0  || _aImages.Front().sName.Empty() )
	{
		return E_FAIL;
	}

	bool	b_save_mm = _bMipmaps;

	for (uint i = 1; i < _aImages.Count(); ++i)
	{
		if ( _aImages[i].sImage.aData.Empty() || _aImages[i].sImage.aMipMaps.Empty() ||
			 _aImages[i].sImage.eFormat == 0 || _aImages[i].sName.Empty() )
		{
			b_save_mm = false;
			break;
		}
	}

	b_save_mm &= GetNumMipmaps( _aImages.Front().sImage.uLength ) == _aImages.Count();

	TImage1D	s_image;

	s_image.uLength		= _aImages.Front().sImage.uLength;
	s_image.bCompressed = _bCompressed;
	s_image.eFormat		= _eFormat;
	s_image.eSwizzle	= _eSwizzle;

	if ( _bUseCompression )
		_bUseCompression = Image::Compress( s_image.eFormat );

	if ( b_save_mm )
	{
		for (uint i = 0; i < _aImages.Count(); ++i)
		{
			s_image.aData.Append( _aImages[i].sImage.aData.ptr(), _aImages[i].sImage.aMipMaps.Front() );
			s_image.aMipMaps.PushBack( _aImages[i].sImage.aMipMaps.Front() );
		}
	}
	else
	{
		s_image.aData.Copy( _aImages.Front().sImage.aData.ptr(), _aImages.Front().sImage.aMipMaps.Front() );
		s_image.aMipMaps.PushBack( _aImages.Front().sImage.aMipMaps.Front() );
	}

	bool	ret = Loaders::SaveImage1D( pFile, s_image, e_image::TEXTURE_1D );

	return ret ? S_OK : E_FAIL;
}


HRESULT CTexture1D::GetFormat(TTextureFormat &sFormat)
{
	sFormat.uColorFormat	= ( _eFormat & e_tex_format::_CF_MASK ) >> e_tex_format::_CF_OFFSET;
	sFormat.uInternalFormat	= ( _eFormat & e_tex_format::_CIF_MASK ) >> e_tex_format::_CIF_OFFSET;
	sFormat.uValueType		= ( _eFormat & e_tex_format::_T_MASK ) >> e_tex_format::_T_OFFSET;
	
	GLint	swizzle[4];
	Image::GetSwizzle( _eSwizzle, swizzle );
	
	sFormat.sChannels.R		= GetChannel( swizzle[0] );
	sFormat.sChannels.G		= GetChannel( swizzle[1] );
	sFormat.sChannels.B		= GetChannel( swizzle[2] );
	sFormat.sChannels.A		= GetChannel( swizzle[3] );

	sFormat.bMipmaps		= _bMipmaps;
	sFormat.bCompressed		= _bUseCompression;
	
	return S_OK;
}


HRESULT CTexture1D::SetFormat(const TTextureFormat &sFormat)
{
	_eFormat	&= ~e_tex_format::_CIF_MASK;
	_eFormat	|= ( sFormat.uInternalFormat << e_tex_format::_CIF_OFFSET ) & e_tex_format::_CIF_MASK;

	GLint	swizzle[4] = {
		GetGLChannel( sFormat.sChannels.R ),
		GetGLChannel( sFormat.sChannels.G ),
		GetGLChannel( sFormat.sChannels.B ),
		GetGLChannel( sFormat.sChannels.A )
	};

	Image::SetSwizzle( swizzle, _eSwizzle );

	_bMipmaps		 = sFormat.bMipmaps;
	_bUseCompression = sFormat.bCompressed;

	return S_OK;
}


HRESULT CTexture1D::GetTexType(E_TEXTURE_TYPE &eType)
{
	eType = TT_TEXTURE_1D;
	return S_OK;
}


HRESULT CTexture1D::GetDimension(TPoint3u &sDim)
{
	sDim.x = _aImages.Front().sImage.uLength;
	sDim.y = 0;
	sDim.z = 0;
	return S_OK;
}


HRESULT CTexture1D::RemoveLayer(uint uLayer)
{
	return S_FALSE;
}


HRESULT CTexture1D::RemoveMipmap(uint uLayer, uint uMipmap)
{
	if ( uLayer != 0 || uMipmap >= _aImages.Count() || uMipmap == 0 )
		return E_INVALIDARG;

	_aImages[uMipmap].sImage.Clear();
	_aImages[uMipmap].sName.Clear();
	return S_OK;
}


HRESULT CTexture1D::SwapLayers(uint uFrom, uint uTo)
{
	return S_FALSE;
}



//-------------------------- CTexture2D ---------------------------//

CTexture2D::CTexture2D():
	_eFormat(e_tex_format::UNKNOWN),
	_bCompressed(false),	_eSwizzle(e_tex_swizzle::AUTO),
	_bMipmaps(false),		_bUseCompression(false)
{
}


CTexture2D::~CTexture2D()
{
}


HRESULT CTexture2D::SetImage(const char *pName)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage2D	s_image;

	if ( !SYSTEMS->pIL->GetTexture2D( s_image ) )
		return E_FAIL;

	_aImages.Clear();
	_aImages.PushFront( TImage2DExt( s_image, pName ) );
	_aImages.Resize( GetNumMipmaps( s_image.vTexDim ) );
	_eFormat		 = s_image.eFormat;
	_eSwizzle		 = s_image.eSwizzle;
	_bCompressed	 = s_image.bCompressed;
	_bUseCompression = _bCompressed;
	_bMipmaps		 = false;
	return S_OK;
}


HRESULT CTexture2D::AddLayer(const char *pName, uint &uLayer)
{
	return S_FALSE;
}


HRESULT CTexture2D::AddMipmap(const char *pName, uint uLayer, uint &uMM)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage2D	s_image;

	if ( !SYSTEMS->pIL->GetTexture2D( s_image ) )
		return E_FAIL;

	int	i_level = 0;

	if ( !Image::ComparePixelFormats( s_image.eFormat, _eFormat ) || s_image.bCompressed != _bCompressed ||
		 !GetMipmapLevel( _aImages.Front().sImage.vTexDim, s_image.vTexDim, i_level ) )
	{
		return E_FAIL;
	}

	uMM = i_level;
	_aImages[i_level] = TImage2DExt( s_image, pName );
	return S_OK;
}


HRESULT CTexture2D::GetName(uint uLayer, uint uMipmap, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 ||
		 uLayer != 0 || uMipmap >= _aImages.Count() )
	{
		return E_INVALIDARG;
	}

	if ( uCount > _aImages[uMipmap].sName.Length() )
	{
		memcpy( pBuffer, _aImages[uMipmap].sName.cstr(), _aImages[uMipmap].sName.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CTexture2D::Save(IFilePtr pFile)
{
	if ( _aImages.Empty() || _aImages.Front().sImage.aData.Empty() || _aImages.Front().sImage.aMipMaps.Empty() ||
		 _aImages.Front().sImage.eFormat == 0 || _aImages.Front().sName.Empty() )
	{
		return E_FAIL;
	}

	bool	b_save_mm = _bMipmaps;

	for (uint i = 1; i < _aImages.Count(); ++i)
	{
		if ( _aImages[i].sImage.aData.Empty() || _aImages[i].sImage.aMipMaps.Empty() ||
			 _aImages[i].sImage.eFormat == 0  || _aImages[i].sName.Empty() )
		{
			b_save_mm = false;
			break;
		}
	}

	b_save_mm &= GetNumMipmaps( _aImages.Front().sImage.vTexDim ) == _aImages.Count();

	TImage2D	s_image;

	s_image.vTexDim		= _aImages.Front().sImage.vTexDim;
	s_image.bCompressed = _bCompressed;
	s_image.eFormat		= _eFormat;
	s_image.eSwizzle	= _eSwizzle;
	
	if ( _bUseCompression )
		_bUseCompression = Image::Compress( s_image.eFormat );

	if ( b_save_mm )
	{
		for (uint i = 0; i < _aImages.Count(); ++i)
		{
			s_image.aData.Append( _aImages[i].sImage.aData.ptr(), _aImages[i].sImage.aMipMaps.Front() );
			s_image.aMipMaps.PushBack( _aImages[i].sImage.aMipMaps.Front() );
		}
	}
	else
	{
		s_image.aData.Copy( _aImages.Front().sImage.aData.ptr(), _aImages.Front().sImage.aMipMaps.Front() );
		s_image.aMipMaps.PushBack( _aImages.Front().sImage.aMipMaps.Front() );
	}

	bool	ret = Loaders::SaveImage2D( pFile, s_image, e_image::TEXTURE_2D );

	return ret ? S_OK : E_FAIL;
}


HRESULT CTexture2D::GetFormat(TTextureFormat &sFormat)
{
	sFormat.uColorFormat	= ( _eFormat & e_tex_format::_CF_MASK )  >> e_tex_format::_CF_OFFSET;
	sFormat.uInternalFormat	= ( _eFormat & e_tex_format::_CIF_MASK ) >> e_tex_format::_CIF_OFFSET;
	sFormat.uValueType		= ( _eFormat & e_tex_format::_T_MASK )   >> e_tex_format::_T_OFFSET;
	
	GLint	swizzle[4];
	Image::GetSwizzle( _eSwizzle, swizzle );
	
	sFormat.sChannels.R		= GetChannel( swizzle[0] );
	sFormat.sChannels.G		= GetChannel( swizzle[1] );
	sFormat.sChannels.B		= GetChannel( swizzle[2] );
	sFormat.sChannels.A		= GetChannel( swizzle[3] );

	sFormat.bCompressed		= _bUseCompression;
	sFormat.bMipmaps		= _bMipmaps;
	
	return S_OK;
}


HRESULT CTexture2D::SetFormat(const TTextureFormat &sFormat)
{
	_eFormat	&= ~e_tex_format::_CIF_MASK;
	_eFormat	|= ( sFormat.uInternalFormat << e_tex_format::_CIF_OFFSET ) & e_tex_format::_CIF_MASK;

	GLint	swizzle[4] = {
		GetGLChannel( sFormat.sChannels.R ),
		GetGLChannel( sFormat.sChannels.G ),
		GetGLChannel( sFormat.sChannels.B ),
		GetGLChannel( sFormat.sChannels.A )
	};

	Image::SetSwizzle( swizzle, _eSwizzle );

	_bMipmaps		 = sFormat.bMipmaps;
	_bUseCompression = sFormat.bCompressed;

	return S_OK;
}


HRESULT CTexture2D::GetTexType(E_TEXTURE_TYPE &eType)
{
	eType = TT_TEXTURE_2D;
	return S_OK;
}


HRESULT CTexture2D::GetDimension(TPoint3u &sDim)
{
	sDim.x	= _aImages.Front().sImage.vTexDim.x;
	sDim.y	= _aImages.Front().sImage.vTexDim.y;
	sDim.z	= 0;
	return S_OK;
}


HRESULT CTexture2D::RemoveLayer(uint uLayer)
{
	return S_FALSE;
}


HRESULT CTexture2D::RemoveMipmap(uint uLayer, uint uMipmap)
{
	if ( uLayer != 0 || uMipmap >= _aImages.Count() || uMipmap == 0 )
		return E_INVALIDARG;

	_aImages[uMipmap].sImage.Clear();
	_aImages[uMipmap].sName.Clear();
	return S_OK;
}


HRESULT CTexture2D::SwapLayers(uint uFrom, uint uTo)
{
	return S_FALSE;
}



//--------------------------- CTexture3D --------------------------//

CTexture3D::CTexture3D():
	_eFormat(e_tex_format::UNKNOWN),
	_bCompressed(false),	_eSwizzle(e_tex_swizzle::AUTO),
	_bMipmaps(false),		_bUseCompression(false)
{
}


CTexture3D::~CTexture3D()
{
}
	

HRESULT CTexture3D::SetImage(const char *pName)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage3D	s_image;

	if ( !SYSTEMS->pIL->GetTexture3D( s_image ) )
		return E_FAIL;

	_aImages.Clear();
	_aImages.PushFront( TImage3DExt( s_image, pName ) );
	_aImages.Resize( GetNumMipmaps( s_image.vTexDim ) );
	_eFormat		 = s_image.eFormat;
	_eSwizzle		 = s_image.eSwizzle;
	_bCompressed	 = s_image.bCompressed;
	_bUseCompression = _bCompressed;
	_bMipmaps		 = false;
	return S_OK;
}


HRESULT CTexture3D::AddLayer(const char *pName, uint &uLayer)
{
	return S_FALSE;
}


HRESULT CTexture3D::AddMipmap(const char *pName, uint uLayer, uint &uMM)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	TImage3D	s_image;

	if ( !SYSTEMS->pIL->GetTexture3D( s_image ) )
		return E_FAIL;

	int	i_level = 0;

	if ( !Image::ComparePixelFormats( s_image.eFormat, _eFormat ) || s_image.bCompressed != _bCompressed ||
		 !GetMipmapLevel( _aImages.Front().sImage.vTexDim, s_image.vTexDim, i_level ) )
	{
		return E_FAIL;
	}

	uMM = i_level;
	_aImages[i_level] = TImage3DExt( s_image, pName );
	return S_OK;
}


HRESULT CTexture3D::GetName(uint uLayer, uint uMipmap, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 ||
		 uLayer != 0 || uMipmap >= _aImages.Count() )
	{
		return E_INVALIDARG;
	}

	if ( uCount > _aImages[uMipmap].sName.Length() )
	{
		memcpy( pBuffer, _aImages[uMipmap].sName.cstr(), _aImages[uMipmap].sName.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CTexture3D::Save(IFilePtr pFile)
{
	if ( _aImages.Front().sImage.aData.Empty() || _aImages.Front().sImage.aMipMaps.Empty() ||
		 _aImages.Front().sImage.eFormat == 0  || _aImages.Front().sName.Empty() )
	{
		return E_FAIL;
	}

	bool	b_save_mm = _bMipmaps;

	for (uint i = 1; i < _aImages.Count(); ++i)
	{
		if ( _aImages[i].sImage.aData.Empty() || _aImages[i].sImage.aMipMaps.Empty() ||
			 _aImages[i].sImage.eFormat == 0 || _aImages[i].sName.Empty() )
		{
			b_save_mm = false;
			break;
		}
	}

	b_save_mm &= GetNumMipmaps( _aImages.Front().sImage.vTexDim ) == _aImages.Count();

	TImage3D	s_image;

	s_image.vTexDim		= _aImages.Front().sImage.vTexDim;
	s_image.bCompressed = _bCompressed;
	s_image.eFormat		= _eFormat;
	s_image.eSwizzle	= _eSwizzle;

	if ( _bUseCompression )
		_bUseCompression = Image::Compress( s_image.eFormat );

	if ( b_save_mm )
	{
		for (uint i = 0; i < _aImages.Count(); ++i)
		{
			s_image.aData.Append( _aImages[i].sImage.aData.ptr(), _aImages[i].sImage.aMipMaps.Front() );
			s_image.aMipMaps.PushBack( _aImages[i].sImage.aMipMaps.Front() );
		}
	}
	else
	{
		s_image.aData.Copy( _aImages.Front().sImage.aData.ptr(), _aImages.Front().sImage.aMipMaps.Front() );
		s_image.aMipMaps.PushBack( _aImages.Front().sImage.aMipMaps.Front() );
	}

	bool	ret = Loaders::SaveImage3D( pFile, s_image, e_image::TEXTURE_3D );

	return ret ? S_OK : E_FAIL;
}


HRESULT CTexture3D::GetFormat(TTextureFormat &sFormat)
{
	sFormat.uColorFormat	= ( _eFormat & e_tex_format::_CF_MASK ) >> e_tex_format::_CF_OFFSET;
	sFormat.uInternalFormat	= ( _eFormat & e_tex_format::_CIF_MASK ) >> e_tex_format::_CIF_OFFSET;
	sFormat.uValueType		= ( _eFormat & e_tex_format::_T_MASK ) >> e_tex_format::_T_OFFSET;
	
	GLint	swizzle[4];
	Image::GetSwizzle( _eSwizzle, swizzle );
	
	sFormat.sChannels.R		= GetChannel( swizzle[0] );
	sFormat.sChannels.G		= GetChannel( swizzle[1] );
	sFormat.sChannels.B		= GetChannel( swizzle[2] );
	sFormat.sChannels.A		= GetChannel( swizzle[3] );

	sFormat.bCompressed		= _bUseCompression;
	sFormat.bMipmaps		= _bMipmaps;
	
	return S_OK;
}


HRESULT CTexture3D::SetFormat(const TTextureFormat &sFormat)
{
	_eFormat	&= ~e_tex_format::_CIF_MASK;
	_eFormat	|= ( sFormat.uInternalFormat << e_tex_format::_CIF_OFFSET ) & e_tex_format::_CIF_MASK;

	GLint	swizzle[4] = {
		GetGLChannel( sFormat.sChannels.R ),
		GetGLChannel( sFormat.sChannels.G ),
		GetGLChannel( sFormat.sChannels.B ),
		GetGLChannel( sFormat.sChannels.A )
	};

	Image::SetSwizzle( swizzle, _eSwizzle );

	_bUseCompression	= sFormat.bCompressed;
	_bMipmaps			= sFormat.bMipmaps;

	return S_OK;
}


HRESULT CTexture3D::GetTexType(E_TEXTURE_TYPE &eType)
{
	eType = TT_TEXTURE_3D;
	return S_OK;
}


HRESULT CTexture3D::GetDimension(TPoint3u &sDim)
{
	sDim.x = _aImages.Front().sImage.vTexDim.x;
	sDim.y = _aImages.Front().sImage.vTexDim.y;
	sDim.z = _aImages.Front().sImage.vTexDim.z;
	return S_OK;
}


HRESULT CTexture3D::RemoveLayer(uint uLayer)
{
	return S_FALSE;
}


HRESULT CTexture3D::RemoveMipmap(uint uLayer, uint uMipmap)
{
	if ( uLayer != 0 || uMipmap >= _aImages.Count() || uMipmap == 0 )
		return E_INVALIDARG;

	_aImages[uMipmap].sImage.Clear();
	_aImages[uMipmap].sName.Clear();
	return S_OK;
}


HRESULT CTexture3D::SwapLayers(uint uFrom, uint uTo)
{
	return S_FALSE;
}



//------------------------- CTexture2DArr -------------------------//

E_TEXTURE_TYPE GetCorrectImageType(E_TEXTURE_TYPE eType)
{
	if ( eType == TT_TEXTURE_CUBE_MAP ||
		 eType == TT_TEXTURE_3D ||
		 eType == TT_TEXTURE_2D_ARRAY ||
		 eType == TT_TEXTURE_CUBE_MAP_ARRAY )
	{
		return eType;
	}
	return TT_NONE;
}


CTexture2DArr::CTexture2DArr(E_TEXTURE_TYPE eType):
	_eFormat(e_tex_format::UNKNOWN),
	_bCompressed(false),	_eSwizzle(e_tex_swizzle::AUTO),
	_bMipmaps(false),		_bUseCompression(false),
	_eType( GetCorrectImageType( eType ) )
{
}


CTexture2DArr::~CTexture2DArr()
{
}
	

HRESULT CTexture2DArr::SetImage(const char *pName)
{
	if ( pName == nullptr )
		return E_INVALIDARG;

	E_TEXTURE_TYPE	eType = SYSTEMS->pIL->GetTexType();
	
	_aImages.Clear();

	if ( eType == TT_TEXTURE_2D )
	{
		TImage2D	s_image;

		if ( !SYSTEMS->pIL->GetTexture2D( s_image ) )
			return E_FAIL;
		
		_aImages.PushFront( TImage2DAExt( s_image, pName ) );
		_aImages[0].aImages.Resize( GetNumMipmaps( s_image.vTexDim ) );
		_eFormat		 = s_image.eFormat;
		_eSwizzle		 = s_image.eSwizzle;
		_bCompressed	 = s_image.bCompressed;
		_bUseCompression = _bCompressed;
		_bMipmaps		 = false;
		return S_OK;
	}
	else

	if ( eType == TT_TEXTURE_CUBE_MAP ||
		 eType == TT_TEXTURE_2D_ARRAY ||
		 eType == TT_TEXTURE_CUBE_MAP_ARRAY )
	{
		array<TImage2D>		a_images;

		if ( !SYSTEMS->pIL->GetTexture2DArray( a_images ) || a_images.Empty() )
			return E_FAIL;

		_aImages.Resize( a_images.Count(), false );
	
		_eFormat		= a_images.Front().eFormat;
		_eSwizzle		= a_images.Front().eSwizzle;
		_bCompressed	= a_images.Front().bCompressed;
		const uvec2	dim	= a_images.Front().vTexDim;

		for (uint i = 0; i < a_images.Count(); ++i)
		{
			if ( !Image::ComparePixelFormats( a_images[i].eFormat, _eFormat ) ||
				 Any( a_images[i].vTexDim != dim ) ||
				 a_images[i].bCompressed != _bCompressed )
			{
				LOG( "image params mismatch in image array.", );
				return E_FAIL;
			}

			_aImages[i].aImages.PushFront( TImage2DExt( a_images[i], pName ) );
		}
		return S_OK;
	}

	return E_FAIL;
}


HRESULT CTexture2DArr::AddLayer(const char *pName, uint &uLayer)
{
	if ( pName == nullptr )
		return E_INVALIDARG;
	
	E_TEXTURE_TYPE	eType = SYSTEMS->pIL->GetTexType();

	if ( eType == TT_TEXTURE_2D )
	{
		TImage2D	s_image;

		if ( !SYSTEMS->pIL->GetTexture2D( s_image ) )
			return E_FAIL;

		const TImage2D	&img = _aImages.Front().aImages.Front().sImage;
		
		if ( Any( s_image.vTexDim != img.vTexDim ) ||
			!Image::ComparePixelFormats( s_image.eFormat, _eFormat ) ||
			 s_image.bCompressed != _bCompressed )
		{
			LOG( "image params mismatch.", );
			return E_FAIL;
		}

		uLayer	= _aImages.Count();
		_aImages.PushBack( TImage2DAExt( s_image, pName ) );
		return S_OK;
	}
	else
	
	if ( eType == TT_TEXTURE_CUBE_MAP ||
		 eType == TT_TEXTURE_2D_ARRAY ||
		 eType == TT_TEXTURE_CUBE_MAP_ARRAY )
	{
		array<TImage2D>		a_images;

		if ( !SYSTEMS->pIL->GetTexture2DArray( a_images ) || a_images.Empty() )
			return E_FAIL;

		const TImage2D	&img = _aImages.Front().aImages.Front().sImage;

		uLayer = _aImages.Count();

		for (uint i = 0; i < a_images.Count(); ++i)
		{
			if ( Any( a_images[i].vTexDim != img.vTexDim ) ||
				!Image::ComparePixelFormats( a_images[i].eFormat, _eFormat ) ||
				 a_images[i].bCompressed != _bCompressed )
			{
				LOG( "image params mismath, image ignored.", );
				continue;
			}

			_aImages.PushBack( TImage2DAExt( a_images[i], pName ) );
		}
		return uLayer < _aImages.Count() ? S_OK : E_FAIL;
	}

	return E_FAIL;
}


HRESULT CTexture2DArr::AddMipmap(const char *pName, uint uLayer, uint &uMM)
{
	if ( pName == nullptr || uLayer >= _aImages.Count() )
		return E_INVALIDARG;
	
	TImage2D	s_image;

	if ( !SYSTEMS->pIL->GetTexture2D( s_image ) )
		return E_FAIL;

	int	i_level = 0;

	if ( !Image::ComparePixelFormats( s_image.eFormat, _eFormat ) || s_image.bCompressed != _bCompressed ||
		 !GetMipmapLevel( _aImages.Front().aImages.Front().sImage.vTexDim, s_image.vTexDim, i_level ) )
	{
		return E_FAIL;
	}

	uMM = i_level;
	_aImages[uLayer].aImages[i_level] = TImage2DExt( s_image, pName );
	return S_OK;
}


HRESULT CTexture2DArr::GetName(uint uLayer, uint uMipmap, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 ||
		uLayer >= _aImages.Count() || uMipmap > _aImages[uLayer].aImages.Count() )
	{
		return E_INVALIDARG;
	}

	const string	&name = _aImages[uLayer].aImages[uMipmap].sName;

	if ( uCount > name.Length() )
	{
		memcpy( pBuffer, name.cstr(), name.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CTexture2DArr::Save(IFilePtr pFile)
{
	if ( _aImages.Empty() || _aImages.Front().aImages.Empty() )
		return E_FAIL;

	const TImage2DExt	&img = _aImages.Front().aImages.Front();

	if ( img.sImage.aData.Empty() || img.sImage.aMipMaps.Empty() ||
		 img.sImage.eFormat == 0  || img.sName.Empty() )
	{
		return E_FAIL;
	}

	bool	b_save_mm	= _bMipmaps;
	uint	u_mm_count	= GetNumMipmaps( _aImages.Front().aImages.Front().sImage.vTexDim );


	for (uint i = 0; i < _aImages.Count(); ++i)
	{
		for (uint j = 0; j < _aImages[i].aImages.Count(); ++j)
		{
			if ( _aImages[i].aImages[j].sImage.aData.Empty() || _aImages[i].aImages[j].sImage.aMipMaps.Empty() ||
				 _aImages[i].aImages[j].sImage.eFormat == 0 || _aImages[i].aImages[j].sName.Empty() )
			{
				b_save_mm = false;
				break;
			}
		}

		if ( !b_save_mm || u_mm_count != _aImages[i].aImages.Count() ) {
			b_save_mm = false;
			break;
		}
	}

	array<TImage2D>		a_images;
	const uvec2			s_dim	= _aImages.Front().aImages.Front().sImage.vTexDim;
	e_tex_format::type	e_fmt	= _eFormat;

	if ( _bUseCompression )
		_bUseCompression = Image::Compress( e_fmt );

	a_images.Resize( _aImages.Count(), false );

	for (uint i = 0; i < _aImages.Count(); ++i)
	{
		a_images[i].vTexDim		= s_dim;
		a_images[i].bCompressed = _bCompressed;
		a_images[i].eFormat		= e_fmt;
		a_images[i].eSwizzle	= _eSwizzle;

		const array< TImage2DExt >	&a_img = _aImages[i].aImages;

		if ( b_save_mm )
		{
			for (uint j = 0; j < a_img.Count(); ++j)
			{
				a_images[i].aData.Append( a_img[j].sImage.aData.ptr(), a_img[j].sImage.aMipMaps.Front() );
				a_images[i].aMipMaps.PushBack( a_img[j].sImage.aMipMaps.Front() );
			}
		}
		else
		{
			a_images[i].aData.Copy( a_img.Front().sImage.aData.ptr(), a_img.Front().sImage.aMipMaps.Front() );
			a_images[i].aMipMaps.PushBack( a_img.Front().sImage.aMipMaps.Front() );
		}
	}

	bool	ret = false;

	switch ( _eType )
	{
		case TT_TEXTURE_CUBE_MAP :
			ret = Loaders::SaveImageArray2D( pFile, a_images, e_image::CUBE_MAP );
			break;

		case TT_TEXTURE_3D :
			ret = Loaders::SaveImageArray2D( pFile, a_images, e_image::TEXTURE_2D_ARRAY );
			break;

		case TT_TEXTURE_2D_ARRAY :
			ret = Loaders::SaveImageArray2D( pFile, a_images, e_image::TEXTURE_2D_ARRAY );
			break;

		case TT_TEXTURE_CUBE_MAP_ARRAY :
			ret = Loaders::SaveImageArray2D( pFile, a_images, e_image::CUBEMAP_ARRAY );
			break;

		default:
			return E_FAIL;
	};


	return ret ? S_OK : E_FAIL;
}


HRESULT CTexture2DArr::GetFormat(TTextureFormat &sFormat)
{
	sFormat.uColorFormat	= ( _eFormat & e_tex_format::_CF_MASK ) >> e_tex_format::_CF_OFFSET;
	sFormat.uInternalFormat	= ( _eFormat & e_tex_format::_CIF_MASK ) >> e_tex_format::_CIF_OFFSET;
	sFormat.uValueType		= ( _eFormat & e_tex_format::_T_MASK ) >> e_tex_format::_T_OFFSET;
	
	GLint	swizzle[4];
	Image::GetSwizzle( _eSwizzle, swizzle );
	
	sFormat.sChannels.R		= GetChannel( swizzle[0] );
	sFormat.sChannels.G		= GetChannel( swizzle[1] );
	sFormat.sChannels.B		= GetChannel( swizzle[2] );
	sFormat.sChannels.A		= GetChannel( swizzle[3] );

	sFormat.bCompressed		= _bUseCompression;
	sFormat.bMipmaps		= _bMipmaps;
	
	return S_OK;
}


HRESULT CTexture2DArr::SetFormat(const TTextureFormat &sFormat)
{
	_eFormat	&= ~e_tex_format::_CIF_MASK;
	_eFormat	|= ( sFormat.uInternalFormat << e_tex_format::_CIF_OFFSET ) & e_tex_format::_CIF_MASK;

	GLint	swizzle[4] = {
		GetGLChannel( sFormat.sChannels.R ),
		GetGLChannel( sFormat.sChannels.G ),
		GetGLChannel( sFormat.sChannels.B ),
		GetGLChannel( sFormat.sChannels.A )
	};

	Image::SetSwizzle( swizzle, _eSwizzle );

	_bUseCompression	= sFormat.bCompressed;
	_bMipmaps			= sFormat.bMipmaps;

	return S_OK;
}


HRESULT CTexture2DArr::GetTexType(E_TEXTURE_TYPE &eType)
{
	eType = _eType;
	return S_OK;
}


HRESULT CTexture2DArr::GetDimension(TPoint3u &sDim)
{
	sDim.x = _aImages.Front().aImages.Front().sImage.vTexDim.x;
	sDim.y = _aImages.Front().aImages.Front().sImage.vTexDim.y;
	sDim.z = _aImages.Count();
	return S_OK;
}


HRESULT CTexture2DArr::RemoveLayer(uint uLayer)
{
	if ( uLayer >= _aImages.Count() || _aImages.Count() == 1 )
		return E_INVALIDARG;

	_aImages.Erase( uLayer );
	return S_OK;
}


HRESULT CTexture2DArr::RemoveMipmap(uint uLayer, uint uMipmap)
{
	if ( uLayer >= _aImages.Count() || uMipmap == 0 ||
		 uMipmap >= _aImages[uLayer].aImages.Count() )
	{
		return E_INVALIDARG;
	}

	_aImages[uLayer].aImages[uMipmap].sImage.Clear();
	_aImages[uLayer].aImages[uMipmap].sName.Clear();
	return S_OK;
}


HRESULT CTexture2DArr::SwapLayers(uint uFrom, uint uTo)
{
	if ( uFrom >= _aImages.Count() || uTo >= _aImages.Count() || uFrom == uTo )
		return E_INVALIDARG;

	_aImages.Swap( uFrom, uTo );
	return S_OK;
}



//----------------------- CTextureConverter -----------------------//

CTextureConverter::CTextureConverter():
	_pTex(nullptr), _eType(TT_NONE)
{
}


CTextureConverter::~CTextureConverter()
{
	if ( _pTex != nullptr )
		delete _pTex;
}


HRESULT CALLBACK CTextureConverter::GetType(E_SUBSYSTEM_TYPE &eType)
{
	eType = SST_TEXTURE_CONVERTER;
	return S_OK;
}


HRESULT CALLBACK CTextureConverter::GetSupportedFormats(char *pBuffer, uint uCount)
{
	static const string		s_sFormats = string("images|")
											<< "*.jpg;" << "*.bmp;" << "*.tga;"
											<< "*.gif;" << "*.tif";

	if ( pBuffer == nullptr || uCount == 0 )
		return E_INVALIDARG;

	if ( uCount > s_sFormats.Length() )
	{
		memcpy( pBuffer, s_sFormats.cstr(), s_sFormats.Size() );
		return S_OK;
	}
	return S_FALSE;
}


HRESULT CALLBACK CTextureConverter::Create(E_TEXTURE_TYPE eType)
{
	Delete();

	_eType = eType;

	if ( eType == TT_TEXTURE_1D )				_pTex = new CTexture1D();		else
	if ( eType == TT_TEXTURE_2D )				_pTex = new CTexture2D();		else
	if ( eType == TT_TEXTURE_3D )				_pTex = new CTexture3D();		else
	if ( eType == TT_TEXTURE_2D_ARRAY ||
		 eType == TT_TEXTURE_CUBE_MAP ||
		 eType == TT_TEXTURE_CUBE_MAP_ARRAY )	_pTex = new CTexture2DArr( eType );
	else
		return E_FAIL;

	return S_OK;
}


HRESULT CALLBACK CTextureConverter::Delete()
{
	if ( _pTex != nullptr ) {
		delete _pTex;
		_pTex = nullptr;
	}

	_eType = TT_NONE;
	return S_OK;
}


HRESULT CALLBACK CTextureConverter::Load(const char *pFileName)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	E_TEXTURE_TYPE	e_type = TT_NONE;

	if ( SYSTEMS->pIL->LoadTexture( pFileName, e_type ) )
	{
		HRESULT	ret = _pTex->SetImage( String::GetFileName( pFileName ) );
		SYSTEMS->pIL->DeleteTexture();
		return ret;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::Save(const char *pFileName)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	if ( pFileName == nullptr )
		return E_INVALIDARG;

	IFilePtr	p_file = nullptr;

	if ( CORE->LoadFile( pFileName, p_file, e_file_open_flags::DEF_WRITE ) != S_OK )
		return E_FAIL;

	return _pTex->Save( p_file );
}


HRESULT CALLBACK CTextureConverter::GetInternalFormatName(uint uFormat, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 || uFormat == 0 )
		return E_INVALIDARG;

	const char	*p_name = Image::GetInternalFormatName( (uFormat << e_tex_format::_CIF_OFFSET) );

	if ( p_name != nullptr )
	{
		usize	len = strlen( p_name );

		if ( uCount > len ) {
			memcpy( pBuffer, p_name, len );
			pBuffer[len] = '\0';
			return S_OK;
		}
		return S_FALSE;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::GetColorFormatName(uint uFormat, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 || uFormat == 0 )
		return E_INVALIDARG;

	const char	*p_name = Image::GetColorFormatName( (uFormat << e_tex_format::_CF_OFFSET) );

	if ( p_name != nullptr )
	{
		usize	len = strlen( p_name );

		if ( uCount > len ) {
			memcpy( pBuffer, p_name, len );
			pBuffer[len] = '\0';
			return S_OK;
		}
		return S_FALSE;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::GetColorTypeName(uint uType, char *pBuffer, uint uCount)
{
	if ( pBuffer == nullptr || uCount == 0 || uType == 0 )
		return E_INVALIDARG;

	const char	*p_name = Image::GetColorValueTypeName( (uType << e_tex_format::_T_OFFSET) );

	if ( p_name != nullptr )
	{
		usize	len = strlen( p_name );

		if ( uCount > len ) {
			memcpy( pBuffer, p_name, len );
			pBuffer[len] = '\0';
			return S_OK;
		}
		return S_FALSE;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::GetName(uint uLayer, uint uMipmap, char *pBuffer, uint uCount)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->GetName( uLayer, uMipmap, pBuffer, uCount );
}


HRESULT CALLBACK CTextureConverter::GetFormat(TTextureFormat &sFormat)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->GetFormat( sFormat );
}


HRESULT CALLBACK CTextureConverter::SetFormat(const TTextureFormat &sFormat)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->SetFormat( sFormat );
}


HRESULT CALLBACK CTextureConverter::GetTexType(E_TEXTURE_TYPE &eType)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->GetTexType( eType );
}


HRESULT CALLBACK CTextureConverter::GetDimension(TPoint3u &sDim)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->GetDimension( sDim );
}


HRESULT CALLBACK CTextureConverter::GetInfo(char *pBuffer, uint uCount)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	if ( pBuffer == nullptr || uCount == 0 )
		return E_INVALIDARG;

	string				str;
	TPoint3u			s_dim;
	e_tex_format::type	e_fmt = _pTex->GetTexFormat();

	_pTex->GetDimension( s_dim );

	str << "width:        " << s_dim.x << "\x00D\x00A";
	
	if ( s_dim.y != 0 )
	str << "height:       " << s_dim.y << "\x00D\x00A";

	if ( s_dim.z != 0 )
	str << "depth:        " << s_dim.z << "\x00D\x00A";

	//str << "format:       " << Image::GetInternalFormatName( e_fmt ) << "\x00D\x00A";
	str << "color format: " << Image::GetColorFormatName( e_fmt ) << "\x00D\x00A";
	str << "color type:   " << Image::GetColorValueTypeName( e_fmt ) << "\x00D\x00A";

	if ( uCount > str.Length() )
	{
		memcpy( pBuffer, str.cstr(), str.Size() );
		return S_OK;
	}

	return S_FALSE;
}


HRESULT CALLBACK CTextureConverter::LoadMipmap(const char *pFileName, uint uLayer, uint &uMipmapNumb)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	E_TEXTURE_TYPE	e_type = TT_NONE;

	if ( SYSTEMS->pIL->LoadTexture( pFileName, e_type ) )
	{
		HRESULT	ret = _pTex->AddMipmap( String::GetFileName( pFileName ), uLayer, uMipmapNumb );
		SYSTEMS->pIL->DeleteTexture();
		return ret;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::LoadLayer(const char *pFileName, uint &uLayer)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	E_TEXTURE_TYPE	e_type = TT_NONE;

	if ( SYSTEMS->pIL->LoadTexture( pFileName, e_type ) )
	{
		HRESULT	ret = _pTex->AddLayer( String::GetFileName( pFileName ), uLayer );
		SYSTEMS->pIL->DeleteTexture();
		return ret;
	}
	return E_FAIL;
}


HRESULT CALLBACK CTextureConverter::RemoveLayer(uint uLayer)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->RemoveLayer( uLayer );
}


HRESULT CALLBACK CTextureConverter::RemoveMipmap(uint uLayer, uint uMipmap)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->RemoveMipmap( uLayer, uMipmap );
}


HRESULT CALLBACK CTextureConverter::SwapLayers(uint uFrom, uint uTo)
{
	if ( _pTex == nullptr )
		return E_POINTER;

	return _pTex->SwapLayers( uFrom, uTo );
}
